package com.car.datalayer.http;

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;

import com.car.comm.ErrorAgent;
import com.car.comm.Util;
import com.car.comm.Variable;
import com.car.comm.Assert;
import com.car.comm.Logger;
import com.car.protocol.HttpProtocol;

/**
 * 利用多线程实现的HTTP引擎
 * @author Xairy
 */
public class HttpEngineMultiThread implements IHttpEngine{
	private class Request{
		public RequestId requestId;
		public CHttpRequest cHttpRequest;
		public IHttpResponseHandler handler;
		@SuppressWarnings("unused")
		public int levelnum;    // 请求的优先级数值
		
		public Request(int level){
			Assert.TRUE(level >= Variable.levelLow &&
						level < Variable.levelNum, 
						"Unexpected Level " + level);
			
			if(level == Variable.levelLow){
				this.levelnum = 0;
			}else if(level == Variable.levelNormal){
				this.levelnum = 5;
			}else if(level == Variable.levelHigh){
				this.levelnum = 13;
			}
		}
		
		@Override
		public boolean equals(Object obj){
			if(obj == null ||
			   this.getClass() != obj.getClass()){
				return false;
			}
			if(this == obj){
				return true;
			}
			
			Request req = (Request)obj;
			return req.requestId.equals(this.requestId);
		}
		
		@Override
		public int hashCode(){
			return this.requestId.hashCode();
		}
	}
	
	private class RequestQueue{
		private Queue<Request> queue;
		private Lock mutexLock;
		
		public RequestQueue(){
			queue = new LinkedList<Request>();
			mutexLock = new ReentrantLock();
		}
		
		public void AddRequest(Request req, int level){
			// 先简单的实现
			mutexLock.lock();
			queue.add(req);
			mutexLock.unlock();
		}
		
		public Request CheckRequest()
		{
			mutexLock.lock();
			Request req = queue.poll();
			mutexLock.unlock();
			return req;
		}
		
		public boolean Remove(RequestId reqId){
			mutexLock.lock();
			Request request = new Request(Variable.levelLow);
			request.requestId = reqId;
			boolean result = queue.remove(request);
			mutexLock.unlock();
			return result;
		}
		
		public void Clear(){
			mutexLock.lock();
			queue.clear();
			mutexLock.unlock();
		}
	}
	
	private class HttpWorker extends Thread{
		private boolean stop = false;
		
		public HttpWorker(){
			
		}
		
		@Override
		public void run(){
			while(!stop){
				Request req = HttpEngineMultiThread.this.ChooseRequest();
				if(req == null){
					// 简单的轮训方式
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						// 被中断继续
					}
					continue;
				}
				
				HttpClient client = new DefaultHttpClient(HttpParams());
				
				HttpRequestBase method = null;
				HttpResponse httpResp = null;
				try {
					// 填充相应的方法
					if(req.cHttpRequest.getMethod() == CHttpRequest.EHttpGet){
						method = new HttpGet(req.cHttpRequest.getUri());
					}else if(req.cHttpRequest.getMethod() ==  CHttpRequest.EHttpPost){
						method = new HttpPost(req.cHttpRequest.getUri());
						
						CHttpContentProvider provider = new CHttpContentProvider(req.cHttpRequest);
						((HttpPost)method).setEntity(new EntityTemplate(provider));
					}else{
						Assert.TRUE(false, "Unexpected http method");
					}
					
					// 填充头部
					SetHeaders(method, req.cHttpRequest);
					
					httpResp = client.execute(method);
					
					int statusCode = httpResp.getStatusLine().getStatusCode();
					CHttpResponse resp = new CHttpResponse(statusCode);
					Header[] headers = httpResp.getAllHeaders();
					for(int index = 0; index < headers.length; ++index){
						resp.SetHeader(headers[index].getName(), headers[index].getValue());
					}
					
					if(statusCode == 200){
						HttpEntity entity = httpResp.getEntity();
						InputStream stream = entity.getContent();
						resp.setResponseBody(Util.toByteArray(stream));
					}
					HandleResponseCallback(req, 0, resp);
				} catch (ClientProtocolException e) {
					HandleResponseCallback(req, ErrorAgent.EHttpClientProtocolError, null);
					continue;
				} catch (IOException e) {
					HandleResponseCallback(req, ErrorAgent.EHttpIoError, null);
					continue;
				}
				client = null;
			}
		}
		
		private void SetHeaders(HttpRequestBase method, CHttpRequest req){
			if(req.getHeader(HttpProtocol.httpUserAgent).equals("")){
				// 设置一个默认的User-Agent
				method.setHeader(HttpProtocol.httpUserAgent, "HTTP[CAR ENGINE]");
			}
			if(req.getHeader(HttpProtocol.httpAccept).equals("")){
				// 设置接受所有类型的请求
				method.setHeader(HttpProtocol.httpAccept, "*/*");
			}
			for(Entry<String, String> entry : req.getHeaders().entrySet()){
				method.setHeader(entry.getKey(), entry.getValue());
			}
		}
		
		private BasicHttpParams HttpParams(){
			BasicHttpParams params = new BasicHttpParams();
			
			HttpConnectionParams.setConnectionTimeout(params,
								Variable.httpConnectionTimeoutSec * 1000);
			HttpConnectionParams.setSoTimeout(params, 
								Variable.httpReadStreamTimeOutSec * 1000);
			
			return params;
		}
		
		private void HandleResponseCallback(Request req, 
											int innerErrCode,
											CHttpResponse resp){
			if(req.handler != null){
				try{
					req.handler.Response(req.requestId, innerErrCode, resp);
				}catch(Exception e){
					Logger.LogRun("HandleResponseCallback[requestId=" + req.requestId
							       + "] reponse throw Exception:" + e.getMessage());
				}
			}
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}
	}
	
	private RequestQueue reqQueue;
	private HttpWorker[] workers;
	
	
	/**
	 *  调度算法，每个队列的元素都有初始优先级，每次发送一个请求，优先级增加
	 * @return
	 */
	private synchronized Request ChooseRequest(){
		return reqQueue.CheckRequest();
	}
	
	public HttpEngineMultiThread(int workerNum){
		reqQueue = new RequestQueue();
		workers = new HttpWorker[workerNum];
		
		for(int index = 0; index < workerNum; ++index){
			workers[index] = new HttpWorker();
			workers[index].start();
			workers[index].setName("HTTP " + index);
		}
	}
	
	
	@Override
	public RequestId SendRequest(int level,
								 CHttpRequest request, 
								 IHttpResponseHandler handler) {
		Assert.TRUE(request.getUri().length() > 0, "NO URI SET");
		RequestId id = RequestId.CreateId(level);
		Request req = new Request(level);
		req.requestId = id;
		req.cHttpRequest = request;
		req.handler = handler;
		reqQueue.AddRequest(req, level);
		return id;
	}

	@Override
	public void Cancel(RequestId reqId) {
		// 只对队列中的请求进行回收，线程中的请求很难控制
		boolean result = reqQueue.Remove(reqId);
		Logger.LogRun("CancelRequest =" + reqId + " CancelResult =" + result);
	}


	@Override
	public void CancelAll() {
		Logger.LogRun("Cancel All");
		reqQueue.Clear();
	}

	@Override
	public void Stop() {
		reqQueue.Clear();
		for(int index = 0; index < workers.length; ++index){
			workers[index].setStop(true);
		}
	}

}
